// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_RENDERERS_SKCANVAS_VIDEO_RENDERER_H_
#define MEDIA_RENDERERS_SKCANVAS_VIDEO_RENDERER_H_

#include <stddef.h>
#include <stdint.h>

#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "media/base/media_export.h"
#include "media/base/timestamp_constants.h"
#include "media/base/video_frame.h"
#include "media/base/video_rotation.h"
#include "media/filters/context_3d.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkRefCnt.h"

class SkCanvas;

namespace gfx {
class RectF;
}

namespace media {

// Handles rendering of VideoFrames to SkCanvases.
class MEDIA_EXPORT SkCanvasVideoRenderer {
public:
    SkCanvasVideoRenderer();
    ~SkCanvasVideoRenderer();

    // Paints |video_frame| on |canvas|, scaling and rotating the result to fit
    // dimensions specified by |dest_rect|.
    // If the format of |video_frame| is PIXEL_FORMAT_NATIVE_TEXTURE, |context_3d|
    // must be provided.
    //
    // Black will be painted on |canvas| if |video_frame| is null.
    void Paint(const scoped_refptr<VideoFrame>& video_frame,
        SkCanvas* canvas,
        const gfx::RectF& dest_rect,
        SkPaint& paint,
        VideoRotation video_rotation,
        const Context3D& context_3d);

    // Copy |video_frame| on |canvas|.
    // If the format of |video_frame| is PIXEL_FORMAT_NATIVE_TEXTURE, |context_3d|
    // must be provided.
    void Copy(const scoped_refptr<VideoFrame>& video_frame,
        SkCanvas* canvas,
        const Context3D& context_3d);

    // Convert the contents of |video_frame| to raw RGB pixels. |rgb_pixels|
    // should point into a buffer large enough to hold as many 32 bit RGBA pixels
    // as are in the visible_rect() area of the frame.
    static void ConvertVideoFrameToRGBPixels(const media::VideoFrame* video_frame,
        void* rgb_pixels,
        size_t row_bytes);

    // Copy the contents of texture of |video_frame| to texture |texture|.
    // |level|, |internal_format|, |type| specify target texture |texture|.
    // The format of |video_frame| must be VideoFrame::NATIVE_TEXTURE.
    // Assumes |texture| has already been allocated with the appropriate
    // size and a compatible format, internal format and type; this is
    // effectively a "TexSubImage" operation.
    static void CopyVideoFrameSingleTextureToGLTexture(
        gpu::gles2::GLES2Interface* gl,
        VideoFrame* video_frame,
        unsigned int texture,
        bool premultiply_alpha,
        bool flip_y);

    // Copy the contents of texture of |video_frame| to texture |texture| in
    // context |destination_gl|.
    // |level|, |internal_format|, |type| specify target texture |texture|.
    // The format of |video_frame| must be VideoFrame::NATIVE_TEXTURE.
    // |context_3d| has a GrContext that may be used during the copy.
    // Assumes |texture| has already been allocated with the appropriate
    // size and a compatible format, internal format and type; this is
    // effectively a "TexSubImage" operation.
    // Returns true on success.
    bool CopyVideoFrameTexturesToGLTexture(
        const Context3D& context_3d,
        gpu::gles2::GLES2Interface* destination_gl,
        const scoped_refptr<VideoFrame>& video_frame,
        unsigned int texture,
        bool premultiply_alpha,
        bool flip_y);

    // Converts unsigned 16-bit value to target |format| for Y16 format and
    // calls WebGL texImage2D.
    // |level|, |internal_format|, |format|, |type| are WebGL texImage2D
    // parameters.
    // Returns false if there is no implementation for given parameters.
    static bool TexImage2D(unsigned target,
        gpu::gles2::GLES2Interface* gl,
        VideoFrame* video_frame,
        int level,
        int internalformat,
        unsigned format,
        unsigned type,
        bool flip_y,
        bool premultiply_alpha);

    // Converts unsigned 16-bit value to target |format| for Y16 format and
    // calls WebGL texSubImage2D.
    // |level|, |format|, |type|, |xoffset| and |yoffset| are texSubImage2D
    // parameters.
    // Returns false if there is no implementation for given parameters.
    static bool TexSubImage2D(unsigned target,
        gpu::gles2::GLES2Interface* gl,
        VideoFrame* video_frame,
        int level,
        unsigned format,
        unsigned type,
        int xoffset,
        int yoffset,
        bool flip_y,
        bool premultiply_alpha);

    // In general, We hold the most recently painted frame to increase the
    // performance for the case that the same frame needs to be painted
    // repeatedly. Call this function if you are sure the most recent frame will
    // never be painted again, so we can release the resource.
    void ResetCache();

    void CorrectLastImageDimensions(const SkIRect& visible_rect);

    // Used for unit test.
    SkISize LastImageDimensionsForTesting();

private:
    // Update the cache holding the most-recently-painted frame. Returns false
    // if the image couldn't be updated.
    bool UpdateLastImage(const scoped_refptr<VideoFrame>& video_frame,
        const Context3D& context_3d);

    // Last image used to draw to the canvas.
    sk_sp<SkImage> last_image_;
    // Timestamp of the videoframe used to generate |last_image_|.
    base::TimeDelta last_timestamp_ = media::kNoTimestamp;
    // If |last_image_| is not used for a while, it's deleted to save memory.
    base::DelayTimer last_image_deleting_timer_;

    // Used for DCHECKs to ensure method calls executed in the correct thread.
    base::ThreadChecker thread_checker_;

    // Used for unit test.
    SkISize last_image_dimensions_for_testing_;

    DISALLOW_COPY_AND_ASSIGN(SkCanvasVideoRenderer);
};

} // namespace media

#endif // MEDIA_RENDERERS_SKCANVAS_VIDEO_RENDERER_H_
